<?php
/***********************************************************************
 * @package    	IvanEngine
 * @subpackage 	Core
 * @author     	Ivan <ivanzx@msn.com>
 * @created    	2010-05-19
 ***********************************************************************/
if(defined("IVANENGINE_CORE_VALIDATOR_MAP"))
	die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_CORE_VALIDATOR_MAP", true);

require_once (dirname(__FILE__).'/ValidatorBase.php');

class CValidatorMap extends CValidatorBase
{
	const NO_AllowExtraFields	= 'NO_AllowExtraFields';
	const NO_FilterExtraFields	= 'NO_FilterExtraFields';
	const NM_ExtraFields		= 'NM_ExtraFields';
	const NM_PostMaxSize		= 'NM_PostMaxSize';
	
	protected
    	$m_fields        = array(),
    	$m_preValidator  = null,
    	$m_postValidator = null;
	public function __construct($fields = null, $options = array(), $messages = array())
  	{
	    if ($fields instanceof CMap)
	    {
	      	for($it = $fields->Begin(); $it->IsValid(); $it->Next())
	      	{
	        	$this[$it->Key()] = $it->Data();
	      	}
	    }
	    else if (!is_null($fields))
	    {
	     	 throw new CExceptionInvalidArgument('CValidatorMap constructor takes an array of CValidatorBase objects.');
	    }
	
	    parent::__construct($options, $messages);
  	}
  
	protected function Initialize($options = array(), $messages = array())
  	{
  		parent::Initialize($options, $messages);
  		
		$this->AddOption(self::NO_AllowExtraFields, false);
	    $this->AddOption(self::NO_FilterExtraFields, true);
	
	    $this->AddMessage(self::NM_ExtraFields, 'Unexpected extra form field named "%field%".');
	    $this->AddMessage(self::NM_PostMaxSize, 'The form submission cannot be processed. It probably means that you have uploaded a file that is too big.');
  	}
  	
	public function Verify($values)
  	{
    	return $this->DoVerify($values);
  	}
  
	protected function DoVerify($values) 
	{
		if (is_null($values))
		{
		  	$values = array();
		}

		if (!is_array($values))
		{
		  	throw new CExceptionInvalidArgument('You must pass an array parameter to the clean() method');
		}

		$clean  = array();
		$unused = array_keys($this->fields);
		$errorSchema = new CExceptionValidatorMap($this);

		// check that post_max_size has not been reached
		if (isset($_SERVER['CONTENT_LENGTH']) && (int) $_SERVER['CONTENT_LENGTH'] > $this->GetBytes(ini_get(self::NM_PostMaxSize)))
		{
		  	$errorSchema->AddError(new CExceptionValidator($this, self::NM_PostMaxSize));

		  	throw $errorSchema;
		}

		// pre validator
		try
		{
		  $this->PreVerify($values);
		}
		catch (CExceptionValidatorMap $e)
		{
		  	$errorSchema->AddErrors($e);
		}
		catch (CExceptionValidator $e)
		{
		  	$errorSchema->AddError($e);
		}

		// validate given values
		foreach ($values as $name => $value)
		{
		 	// field exists in our schema?
		  	if (!array_key_exists($name, $this->fields))
		  	{
				if (!$this->m_options[self::NO_AllowExtraFields])
				{
			 		$errorSchema->AddError(new CExceptionValidator($this, self::NM_ExtraFields, array('field' => $name)));
				}
				else if (!$this->m_options[self::NO_FilterExtraFields])
				{
			  		$clean[$name] = $value;
				}

				continue;
		  }

		  unset($unused[array_search($name, $unused, true)]);

		  // validate value
		  try
		  {
				$clean[$name] = $this->m_fields[$name]->Verify($value);
		  }
		  catch (CExceptionValidator $e)
		  {
			$clean[$name] = null;

			$errorSchema->AddError($e, (string) $name);
		  }
		}

		// are non given values required?
		foreach ($unused as $name)
		{
		  	// validate value
		  	try
		  	{
				$clean[$name] = $this->m_fields[$name]->Verify(null);
		  	}
		  	catch (CExceptionValidator $e)
		  	{
				$clean[$name] = null;

				$errorSchema->AddError($e, (string) $name);
		  	}
		}

		// post validator
		try
		{
		  	$clean = $this->PostVerify($clean);
		}
		catch (CExceptionValidatorMap $e)
		{
		  	$errorSchema->AddErrors($e);
		}
		catch (CExceptionValidator $e)
		{
		 	 $errorSchema->AddError($e);
		}

		if (count($errorSchema))
		{
		  	throw $errorSchema;
		}

		return $clean;
	}
	
	/**
	* Verify the input values.
	*
	* This method is the first validator executed by DoVerify().
	*
	* It executes the validator returned by GetPreValidator()
	* on the global array of values.
	*
	* @param  array $values  The input values
	*/
	public function PreVerify($values)
	{
		if (is_null($validator = $this->GetPreValidator()))
		{
	  		return;
		}

		$validator->Verify($values);
	}

	/**
	* Verify the input values.
	*
	* This method is the last validator executed by DoVerify().
	*
	* It executes the validator returned by GetPostValidator()
	* on the global array of cleaned values.
	*
	* @param  array $values  The input values
	*/
	public function PostVerify($values)
	{
		if (is_null($validator = $this->GetPostValidator()))
		{
		  	return $values;
		}
	
		return $validator->Verify($values);
	}

	/**
	* Sets the pre validator.
	*
	* @param CValidatorBase $validator  An CValidatorBase instance
	*/
	public function SetPreValidator(CValidatorBase $validator)
	{
		$this->m_preValidator = clone $validator;
	}

	/**
	* Returns the pre validator.
	*
	* @return CValidatorBase A CValidatorBase instance
	*/
	public function GetPreValidator()
	{
		return $this->m_preValidator;
	}

	/**
	* Sets the post validator.
	*
	* @param CValidatorBase $validator  An CValidatorBase instance
	*/
	public function SetPostValidator(CValidatorBase $validator)
	{
		$this->m_postValidator = clone $validator;
	}

	/**
	* Returns the post validator.
	*
	* @return CValidatorBase An CValidatorBase instance
	*/
	public function GetPostValidator()
	{
		return $this->m_postValidator;
	}

	/**
	* Returns an array of fields.
	*
	* @return CValidatorBase An array of CValidatorBase instances
	*/
	public function GetFields()
	{
		return $this->m_fields;
	}

	/**
	* @see sfValidatorBase
	*/
	public function AsString($indent = 0)
	{
		throw new CException('Unable to convert a CValidatorSchema to string.');
	}

	public function __clone()
	{
		foreach ($this->m_fields as $name => $field)
		{
		  	$this->m_fields[$name] = clone $field;
		}
	
		if (!is_null($this->m_preValidator))
		{
		  	$this->m_preValidator = clone $this->m_preValidator;
		}
	
		if (!is_null($this->m_postValidator))
		{
		  	$this->m_postValidator = clone $this->m_postValidator;
		}
	}

	protected function GetBytes($value)
	{
		$value = trim($value);
		switch (strtolower($value[strlen($value) - 1]))
		{
		  	// The 'G' modifier is available since PHP 5.1.0
		  	case 'g':
				$value *= 1024;
		  	case 'm':
				$value *= 1024;
		  	case 'k':
				$value *= 1024;
		}
	
		return $value;
	}
}

?>